﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cjc.Calculation.ExpressionEngine.Excel.Runtime;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Hosting.Providers;
using System.Linq.Expressions;

namespace Cjc.Calculation.ExpressionEngine
{
	public interface ICellContext
	{
		Cell GetCell( Formula formula );
	}

	public class Worksheet : ICellContext
	{
		public virtual Engine Engine { get; private set; }

		private Dictionary<SymbolId, Cell> cells = new Dictionary<SymbolId, Cell>();

		public Worksheet( Engine engine )
		{
			this.Engine = engine;
		}

		public Dictionary<string, Cell> Cells
		{
			get
			{
				lock ( cells )
				{
					return Engine.Formulas.ToDictionary( i => i.Key.ToString(), i => GetCell( i.Key ) );
				}
			}
		}

		public Cell this[ string key ]
		{
			get { return GetCell( Engine[ key ] ); }
		}

		public Cell this[ Formula formula ]
		{
			get { return GetCell( formula ); }
		}

		internal Cell GetCell( SymbolId symbol )
		{
			return GetCell( Engine[ symbol ] );
		}

		public Cell GetCell( Formula formula )
		{
			var symbol = formula.Symbol;

			lock ( cells )
			{
				return cells.ContainsKey( symbol )
					? cells[ symbol ]
					: cells[ symbol ] = CreateCell( formula );
			}
		}

		protected virtual Cell CreateCell( Formula formula )
		{
			return new Cell( this, formula );
		}

		public Formula CreateFormula()
		{
			return Engine.CreateFormula();
		}

		public Formula CreateFormula( string expression )
		{
			return CreateFormula( null, expression );
		}

		public virtual Formula CreateFormula( string key, string expression )
		{
			var formula = CreateFormula();
			formula.Symbol = ( key != null ) ? SymbolTable.StringToCaseInsensitiveId( key ) : SymbolId.Empty;
			formula.ExpressionText = expression;

			return formula;
		}

		public object Evaluate( string expression )
		{
			return CreateFormula( "[Immediate]", expression ).Execute( new WorksheetScope( this ) );
		}
	}
}